//********************************************************
//*                                                      *
//*   Copyright (C) Microsoft. All rights reserved.      *
//*                                                      *
//********************************************************
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Microsoft.MetadirectoryServices;
using System.Diagnostics;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Runtime.InteropServices;

namespace FIM.PowershellMA.EZMA
{

    namespace FimSync_Ezma
    {
        public class EzmaExtension :
        IMAExtensible2CallExport,
        IMAExtensible2CallImport,
        // IMAExtensible2FileImport,
        //IMAExtensible2FileExport,
        //IMAExtensible2GetHierarchy,
        IMAExtensible2GetSchema,
        IMAExtensible2GetCapabilities,
        IMAExtensible2GetParameters
        //IMAExtensible2GetPartitions
        {

            private int m_importDefaultPageSize = 10;
            private int m_importMaxPageSize = 500;
            private int m_exportDefaultPageSize = 10;
            private int m_exportMaxPageSize = 500;
            public string myRemoteServer;
            public string myUserName;
            public string myDomain;
            public string myPowershellFile;
            //public string myImportPSFile;
            //public string myExportPSFile;
            public string mySchemaPSFile;
            public int myImportPageSize;
            public long myImportLastCount = 0;
            public int myExportPageSize;
            public SecureString myPassword;
            public MAExportType MyExportType;
            public Schema schema;
            public OperationType RunStepType;
            public string RunStepPartition;

            public string errorStatusProperty = null;

            PowerShell myPowerShell;
            Runspace myRunSpace;

            //public string errorStatusKey = ".errorStatus";

            private struct myParamStrings
            {
                public const string disableRemoteSchemaUpdate = "Perform schema update localy";
                public const string SchemaFile = "Schema File";
                public const string SchemaFileHelpText = "* Schema powershell script to execute and build schema definition";
                public const string enableRemotePowershell = "Enable remote powershell";
                public const string remoteServer = "Remote Server";
                public const string userName = "User Name";
                public const string domain = "Domain";
                public const string password = "Password";
                public const string exportPowershellFileName = "Global Export Powershell File Name";
                public const string importPowershellFileName = "Global Import Powershell File Name";
                public const string runStepPowershellFileName = "Powershell File Name";
                public const string treatWarningsAsError = "Treat warnings as error";
                public const string forceLocalRunSpace = "Run powershell localy";
                public const string MAExportTypeLabel = "Object Export type";
                public const string MAExportTypeConfigName = "MAExportType";
                public const string MAExportTypeList = "AttributeReplace,AttributeUpdate,ObjectReplace";
            }

            //
            // Constructor
            //
            public EzmaExtension()
            {
            }

            // Populate connection parameters myRemoteServer, myUserNamefrom configParameters.
            public void populateConnectionProperties(KeyedCollection<string, ConfigParameter> configParameters, bool performSchemaUpdate = false)
            {
                myRemoteServer = null;
                myUserName = null;
                myPassword = null;

                bool preventRemotePS=false;

                bool remotePSEnabled = configParameters[myParamStrings.enableRemotePowershell].Value.Equals("1");
                bool remoteSchemeDisabled = configParameters[myParamStrings.disableRemoteSchemaUpdate].Value.Equals("1");
                if (configParameters.Contains(myParamStrings.forceLocalRunSpace)) { preventRemotePS = configParameters[myParamStrings.forceLocalRunSpace].Value.Equals("1"); }

                //Skip if this is called from schemaupdate and remote schema update is disabled
                if (performSchemaUpdate && remoteSchemeDisabled) { return; }

                //Skip if remote powershell is disabled globaly
                if (!remotePSEnabled) { return; }

                //Skip if remote powershell is disabled on run profile
                if (preventRemotePS || !remotePSEnabled) { return; }

                if (configParameters.Contains(myParamStrings.remoteServer) && configParameters.Contains(myParamStrings.userName))
                {

                    myRemoteServer = configParameters[myParamStrings.remoteServer].Value;

                    if (configParameters.Contains(myParamStrings.domain))
                    { myUserName = configParameters[myParamStrings.domain].Value + @"\" + configParameters[myParamStrings.userName].Value; }
                    else { myUserName = configParameters[myUserName].Value; }

                    if (configParameters.Contains(myParamStrings.password))
                    {
                        myPassword = configParameters[myParamStrings.password].SecureValue;
                    }

                }
            }

            // Gets the capabilities
            public MACapabilities Capabilities
            {
                get
                {
                    String extensionDir = Utils.ExtensionsDirectory;

                    MACapabilities myCapabilities = new MACapabilities();

                    myCapabilities.ObjectConfirmation = MAObjectConfirmation.Normal;
                    myCapabilities.ExportPasswordInFirstPass = true;
                    myCapabilities.ConcurrentOperation = true;
                    myCapabilities.ObjectRename = true;
                    myCapabilities.DeleteAddAsReplace = true;
                    myCapabilities.DeltaImport = true;
                    myCapabilities.DistinguishedNameStyle = MADistinguishedNameStyle.Generic;
                    myCapabilities.ExportType = MAExportType.ObjectReplace;
                    myCapabilities.NoReferenceValuesInFirstExport = false;
                    myCapabilities.Normalizations = MANormalizations.None;
                    myCapabilities.FullExport = true;
                    
                    return myCapabilities;

                }

            }

            public IList<ConfigParameterDefinition> GetConfigParameters(KeyedCollection<string, ConfigParameter> configParameters,
                                                                ConfigParameterPage page)
            {
                List<ConfigParameterDefinition> configParametersDefinitions = new List<ConfigParameterDefinition>();

                //MyExportType = MAExportType.ObjectReplace;
                switch (page)
                {

                    case ConfigParameterPage.Connectivity:
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateCheckBoxParameter(myParamStrings.enableRemotePowershell, false));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.remoteServer, "", Environment.MachineName));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.userName, ""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.domain, "", Environment.UserDomainName));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateEncryptedStringParameter(myParamStrings.password, ""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateDividerParameter());
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateCheckBoxParameter(myParamStrings.disableRemoteSchemaUpdate, true));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.SchemaFile, ""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateLabelParameter(myParamStrings.SchemaFileHelpText));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateDropDownParameter(myParamStrings.MAExportTypeConfigName,myParamStrings.MAExportTypeList,false));
                        //this.Capabilities.ExportType = MyExportType;
                        break;

                    case ConfigParameterPage.Global:
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.importPowershellFileName,""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.exportPowershellFileName,""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateDividerParameter());
                        break;

                    case ConfigParameterPage.Partition:
                        break;

                    case ConfigParameterPage.RunStep:
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateCheckBoxParameter(myParamStrings.forceLocalRunSpace, false));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(myParamStrings.runStepPowershellFileName,""));
                        configParametersDefinitions.Add(ConfigParameterDefinition.CreateCheckBoxParameter(myParamStrings.treatWarningsAsError, false));
                        break;

                }

                return configParametersDefinitions;
            }

            public ParameterValidationResult ValidateConfigParameters(KeyedCollection<string, ConfigParameter> configParameters,
                                                                       ConfigParameterPage page)
            {
                FIM.Extensions.Common.Library.networkUtils networkUtils = new FIM.Extensions.Common.Library.networkUtils();
                ParameterValidationResult myResults = new ParameterValidationResult();
                FIM.Extensions.Common.Library.PowershellUtils psUtils = new FIM.Extensions.Common.Library.PowershellUtils();

                switch (page)
                {

                    case ConfigParameterPage.Connectivity:
                        mySchemaPSFile = psUtils.resolvePSFileName(configParameters[myParamStrings.SchemaFile].Value);
                        if (!string.IsNullOrEmpty(mySchemaPSFile))
                        {
                            if (!File.Exists(mySchemaPSFile))
                            {
                                myResults.Code = ParameterValidationResultCode.Failure;
                                myResults.ErrorParameter = myParamStrings.SchemaFile;
                                myResults.ErrorMessage = mySchemaPSFile + ", File not found";
                                break;
                            }
                        }
                        else
                        {
                            myResults.Code = ParameterValidationResultCode.Failure;
                            myResults.ErrorParameter = myParamStrings.SchemaFile;
                            myResults.ErrorMessage = myParamStrings.SchemaFile + " is not specified";
                            break;
                        }

                        populateConnectionProperties(configParameters);

                        //Validate if powershell should run on remote server
                        if (!string.IsNullOrEmpty(myRemoteServer))
                        {
                            if (!networkUtils.pingHost(myRemoteServer) == true)
                            {
                                myResults.Code = ParameterValidationResultCode.Failure;
                                myResults.ErrorParameter = "RemoteServer error";
                                myResults.ErrorMessage = "Faild to connecto to host " + myRemoteServer;
                            }

                            if (string.IsNullOrEmpty(myUserName))
                            {
                                myResults.Code = ParameterValidationResultCode.Failure;
                                myResults.ErrorParameter = myParamStrings.userName;
                                myResults.ErrorMessage = myParamStrings.userName + " not specified";
                            }

                        }

                        if (configParameters.Contains(myParamStrings.MAExportTypeConfigName))
                        {
                            switch (configParameters[myParamStrings.MAExportTypeConfigName].Value)
                            {
                                case "AttributeReplace": MyExportType = MAExportType.AttributeReplace; break;
                                case "AttributeUpdate": MyExportType = MAExportType.AttributeUpdate; break;
                                case "ObjectReplace": MyExportType = MAExportType.ObjectReplace; break;
                            }
                            //this.Capabilities.ExportType = MyExportType;
                        }

                        break;

                    case ConfigParameterPage.Global:
                        //Verify export script
                        if (configParameters.Contains(myParamStrings.exportPowershellFileName))
                        {
                            myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.exportPowershellFileName].Value);
                            if (string.IsNullOrEmpty(myPowershellFile))
                            {
                                myResults.Code = ParameterValidationResultCode.Failure;
                                myResults.ErrorParameter = myParamStrings.exportPowershellFileName;
                                myResults.ErrorMessage = "Unable to find export file : " + configParameters[myParamStrings.exportPowershellFileName].Value;
                            }
                        }

                        //Verify import script
                        if (configParameters.Contains(myParamStrings.importPowershellFileName))
                        {
                            myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.importPowershellFileName].Value);
                            if (string.IsNullOrEmpty(myPowershellFile))
                            {
                                myResults.Code = ParameterValidationResultCode.Failure;
                                myResults.ErrorParameter = myParamStrings.importPowershellFileName;
                                myResults.ErrorMessage = "Unable to find import file : " + configParameters[myParamStrings.importPowershellFileName].Value;
                            }
                        }

                        break;

                    case ConfigParameterPage.RunStep:
                        //Verify export script
                        if (configParameters.Contains(myParamStrings.runStepPowershellFileName))
                        {
                            myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.runStepPowershellFileName].Value);
                            if (string.IsNullOrEmpty(myPowershellFile))
                            {
                                myResults.Code = ParameterValidationResultCode.Failure;
                                myResults.ErrorParameter = myParamStrings.runStepPowershellFileName;
                                myResults.ErrorMessage = "Unable to find export file : " + configParameters[myParamStrings.runStepPowershellFileName].Value;
                            }
                        }
                        break;

                }

                myResults.Code = ParameterValidationResultCode.Success;
                return myResults;

            }

            public Schema GetSchema(KeyedCollection<string, ConfigParameter> configParameters)
            {

                Schema schema = Schema.Create();

                FIM.Extensions.Common.Library.PowershellUtils psUtils = new FIM.Extensions.Common.Library.PowershellUtils();


                try
                {
                    //Get schema from powershell execution result as a hashtable
                    string SchemaPSFile = null;

                    if (configParameters.Contains(myParamStrings.SchemaFile)) { SchemaPSFile = psUtils.resolvePSFileName(configParameters[myParamStrings.SchemaFile].Value); }

                    if (string.IsNullOrEmpty(SchemaPSFile)) { return null; }

                    //Execute schema powershell script
                    myPowerShell = PowerShell.Create();
                    populateConnectionProperties(configParameters, true);
                    myRunSpace = psUtils.OpenRunSpace(myRemoteServer, myUserName, myPassword);
                    psUtils.AddScriptFromFile(SchemaPSFile, ref myPowerShell);
                    ICollection<PSObject> psObjects = null;
                    string errorMsg = null;
                    bool success = psUtils.ExecutePowershell(myPowerShell, ref psObjects, ref errorMsg, true);

                    myRunSpace.Close();
                    myPowerShell.Dispose();

                    if (success)
                    {
                        FIM.Extensions.Common.Library.schemaUtils schemautil = new FIM.Extensions.Common.Library.schemaUtils();

                        foreach (PSObject psObject in psObjects)
                        {
                            Hashtable ht = (Hashtable)psObject.BaseObject;
                            schemautil.AddToSchemaFromHashTable(ht, ref schema);
                        }
                    }

                    EventLog.WriteEntry("FIM.Powershell.XMA", "Schema refresh completed", EventLogEntryType.SuccessAudit);

                    return schema;

                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry("FIM.Powershell.XMA", "Schema refresh failed with following error : " + Environment.NewLine + ex.Message, EventLogEntryType.Error);
                    return null;
                }

            }

            public OpenImportConnectionResults OpenImportConnection(
                                           KeyedCollection<string, ConfigParameter> configParameters,
                                           Schema types,
                                           OpenImportConnectionRunStep importRunStep)
            {
                FIM.Extensions.Common.Library.PowershellUtils psUtils = new FIM.Extensions.Common.Library.PowershellUtils();

                schema = types;

                //Get partition info
                RunStepPartition = importRunStep.StepPartition.DN;

                //Reset last import count
                myImportLastCount = 0;

                //Get import page size
                myImportPageSize = importRunStep.PageSize;

                //Get import type
                RunStepType = importRunStep.ImportType;

                if (configParameters.Contains(myParamStrings.runStepPowershellFileName) && !string.IsNullOrEmpty(configParameters[myParamStrings.runStepPowershellFileName].Value))
                { myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.runStepPowershellFileName].Value); }
                else if (configParameters.Contains(myParamStrings.importPowershellFileName) && !string.IsNullOrEmpty(configParameters[myParamStrings.importPowershellFileName].Value))
                { myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.importPowershellFileName].Value); }
                else { throw new FileNotFoundException("No import powershell script was specified"); }

                populateConnectionProperties(configParameters);
                myPowerShell = PowerShell.Create();
                myRunSpace = psUtils.OpenRunSpace(myRemoteServer, myUserName, myPassword);
                myPowerShell.Runspace = myRunSpace;

                OpenImportConnectionResults openImportConnectionResults = new OpenImportConnectionResults("Custom Data");

                return openImportConnectionResults;
            }

            public GetImportEntriesResults GetImportEntries(GetImportEntriesRunStep importRunStep)
            {
                FIM.Extensions.Common.Library.PowershellUtils psUtils = new FIM.Extensions.Common.Library.PowershellUtils();

                GetImportEntriesResults importReturnInfo;
                List<CSEntryChange> csentries = new List<CSEntryChange>();
                ICollection<PSObject> psObjects = null;
                string errorMsg = null;

                //Execute powershell
                myPowerShell.Commands.Clear();
                psUtils.AddScriptFromFile(myPowershellFile, ref myPowerShell);
                myPowerShell.AddParameter("RunStepType", RunStepType.ToString());
                myPowerShell.AddParameter("RunStepPartition", RunStepPartition);
                myPowerShell.AddParameter("PageSize", myImportPageSize.ToString());
                myPowerShell.AddParameter("ImportLastCount", myImportLastCount.ToString());
                bool executedSuccessfully = psUtils.ExecutePowershell(myPowerShell, ref psObjects, ref errorMsg, false);

                if (!executedSuccessfully) { throw new Exception(errorMsg); }

                //Import result from powershell PSObjects
                foreach (PSObject psObject in psObjects)
                {
                    //Get hashtable from psObject
                    Hashtable ht = (Hashtable)psObject.BaseObject;

                    //Create new CSEntryChange object
                    CSEntryChange csImport = CSEntryChange.Create();

                    //Set objecttype on object to import
                    if (ht.ContainsKey(".objectType")) { csImport.ObjectType = (string)ht[".objectType"]; }

                    //Set ObjectModificationType on object to import
                    switch (ht[".ObjectModificationType"].ToString().ToLower())
                    {
                        case "add": { csImport.ObjectModificationType = ObjectModificationType.Add; break; }
                        case "delete": { csImport.ObjectModificationType = ObjectModificationType.Delete; break; }
                        case "update": { csImport.ObjectModificationType = ObjectModificationType.Update; break; }
                        case "replace": { csImport.ObjectModificationType = ObjectModificationType.Replace; break; }
                        default: { csImport.ErrorCodeImport = MAImportError.ImportErrorInvalidChangeType; break; }
                    }

                    //Cast an ImportErrorInvalidChangeType if operationtype is full and ObjectModificationType not of add or replace
                    //Only add and replace is valid for a full import
                    if ((RunStepType == OperationType.Full && !(csImport.ObjectModificationType.Equals(ObjectModificationType.Add) || csImport.ObjectModificationType.Equals(ObjectModificationType.Replace))))
                    { csImport.ErrorCodeImport = MAImportError.ImportErrorInvalidChangeType; }

                    //Validate if ObjectType and ObjectModificationType is not null
                    if (string.IsNullOrEmpty(csImport.ObjectType)) { csImport.ErrorCodeImport = MAImportError.ImportErrorMissingObjectType; }
                    if (csImport.ObjectModificationType.Equals(null)) { csImport.ErrorCodeImport = MAImportError.ImportErrorMissingChangeType; }

                    if (csImport.ErrorCodeImport.Equals(MAImportError.Success))
                    {
                        foreach (DictionaryEntry entry in ht)
                        {
                            string attributeName = entry.Key.ToString().Split('|')[0];
                            object attributeValue = entry.Value;
                            string attrChangeType = null;
                            if (entry.Key.ToString().Contains("|")) { attrChangeType = entry.Key.ToString().Split('|')[1]; }

                            if (!attributeName.StartsWith("."))
                            {
                                try
                                {
                                    //Handle multivalue operations in an add operation
                                    //Try to replace insert and replace to add otherwise throw an InvalidOperationException
                                    if (csImport.ObjectModificationType != ObjectModificationType.Update && (!string.IsNullOrEmpty(attrChangeType) && attrChangeType.ToLower() != "delete"))
                                        if (attrChangeType.ToLower() == "insert" || attrChangeType.ToLower() == "replace") { attrChangeType = "add"; }
                                        else throw new InvalidOperationException("Attribute " + attributeName + " contained invalid operation " + attrChangeType);
       
                                    switch (csImport.ObjectModificationType)
                                    {
                                        //Sets the attrChangeType from the ObjectModificationType
                                        //If ObjectModificationType is update then the attrChangeType is determined by splitting the attributename using '|'
                                        case ObjectModificationType.Add: attrChangeType = "add"; break;
                                        case ObjectModificationType.Delete: attrChangeType = "delete"; break;
                                        case ObjectModificationType.Replace: attrChangeType = "replace"; break;
                                        case ObjectModificationType.Update: { if (string.IsNullOrEmpty(attrChangeType)) attrChangeType = "replace"; } break;
                                    }

                                    AttributeType attributeType = schema.Types[csImport.ObjectType].Attributes[attributeName].DataType;

                                    //Set the anchor attributes based on the object type schema
                                    if (schema.Types[csImport.ObjectType].AnchorAttributes.Contains(attributeName))
                                    {
                                        if (attributeValue == null) { throw new InvalidDNException("DN Attribute value is null"); }
                                        csImport.AnchorAttributes.Add(AnchorAttribute.Create(attributeName,attributeValue));
                                        csImport.DN = attributeValue.ToString();

                                    }
                                    else
                                    {   
                                        if (schema.Types[csImport.ObjectType].Attributes[attributeName].IsMultiValued)
                                        {
                                            //Attribute is defined as multi-valued in schema

                                            IList<object> values = (object[])entry.Value;
                                            switch (attrChangeType.ToLower())
                                            {
                                                case "add":
                                                    {
                                                        if(values.Count>0) csImport.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(attributeName, values));
                                                        break;
                                                    }

                                                case "replace":
                                                    {
                                                        if (values.Count > 0) csImport.AttributeChanges.Add(AttributeChange.CreateAttributeReplace(attributeName, values));
                                                        else csImport.AttributeChanges.Add(AttributeChange.CreateAttributeDelete(attributeName));
                                                        break;
                                                    }

                                                case "insertvalue":
                                                case "insert":
                                                    {
                                                        List<ValueChange> valueChanges = new List<ValueChange>();
                                                        if (csImport.AttributeChanges.Contains(attributeName))
                                                            foreach (object value in values)
                                                                { csImport.AttributeChanges[attributeName].ValueChanges.Add(ValueChange.CreateValueAdd(value)); }
                                                        else
                                                        {
                                                            foreach (object value in values)
                                                                { valueChanges.Add(ValueChange.CreateValueAdd(value)); }
                                                            csImport.AttributeChanges.Add(AttributeChange.CreateAttributeUpdate(attributeName, valueChanges));
                                                        }
                                                    }
                                                    break;

                                                case "remove":
                                                case "removevalue":
                                                    {
                                                        List<ValueChange> valueChanges = new List<ValueChange>();
                                                        if (csImport.AttributeChanges.Contains(attributeName))
                                                            foreach (object value in values)
                                                            { csImport.AttributeChanges[attributeName].ValueChanges.Add(ValueChange.CreateValueDelete(value)); }
                                                        else
                                                        {
                                                            foreach (object value in values)
                                                            { valueChanges.Add(ValueChange.CreateValueDelete(value)); }
                                                            csImport.AttributeChanges.Add(AttributeChange.CreateAttributeUpdate(attributeName, valueChanges));
                                                        }
                                                    }
                                                    break;

                                                case "delete":
                                                    {
                                                        csImport.AttributeChanges.Add(AttributeChange.CreateAttributeDelete(attributeName));
                                                        break;
                                                    }
                                            }

                                        }
                                        else
                                        {
                                            //Attribute is defined as single-valued in schema
                                            switch (attrChangeType.ToLower())
                                            {
                                                case "add":
                                                    {
                                                        if(!object.ReferenceEquals(attributeValue,null)) csImport.AttributeChanges.Add(AttributeChange.CreateAttributeAdd(attributeName, attributeValue));
                                                        break;
                                                    }
                                                case "replace":
                                                    {
                                                        if (!object.ReferenceEquals(attributeValue, null)) csImport.AttributeChanges.Add(AttributeChange.CreateAttributeReplace(attributeName, attributeValue));
                                                        else csImport.AttributeChanges.Add(AttributeChange.CreateAttributeDelete(attributeName));
                                                        break;
                                                    }
                                                case "delete": csImport.AttributeChanges.Add(AttributeChange.CreateAttributeDelete(attributeName)); break;
                                            }
                                        }
                                    }

                                }
                                catch (Exception e)
                                {
                                    csImport.ErrorCodeImport = MAImportError.ImportErrorInvalidAttributeValue;
                                }
                            }
                        }
                    }

                    //Add object to objects to import
                    csentries.Add(csImport);
                }

                StreamWriter sr = new StreamWriter("C:\\psLog.txt", true);

                foreach (CSEntryChange cs in csentries)
                {
                    sr.WriteLine("DN = " + cs.DN);
                    sr.WriteLine("ObjectModificationType = " + cs.ObjectModificationType.ToString());
                    sr.WriteLine("ObjectType = " + cs.ObjectType);

                    foreach (AnchorAttribute anchAttr in cs.AnchorAttributes)
                    {
                        sr.WriteLine("AcnhorAttr:" + anchAttr.Name + " = " + anchAttr.Value);
                    }
                    foreach (String changedAttr in cs.ChangedAttributeNames)
                    {
                        sr.WriteLine("ChangedAttributeName:" + changedAttr);
                    }
                    foreach (AttributeChange attrChange in cs.AttributeChanges)
                    {
                        foreach (ValueChange vc in attrChange.ValueChanges)
                        {
                            sr.WriteLine(attrChange.Name + " [" + vc.ModificationType.ToString() + "] = " + vc.Value.ToString());
                        }
                    }

                }

                sr.Flush();

                //Add current imported entries to import last count
                myImportLastCount += csentries.Count;

                importReturnInfo = new GetImportEntriesResults();
                importReturnInfo.CSEntries = csentries;
                importReturnInfo.CustomData = "Custom Data";

                if (csentries.Count < myImportPageSize)
                {
                    //No more entries to import from powershell
                    importReturnInfo.MoreToImport = false;
                }
                else
                {
                    //More entries to import could exist from powershell
                    importReturnInfo.MoreToImport = true;
                }

                return importReturnInfo;

            }

            public CloseImportConnectionResults CloseImportConnection(CloseImportConnectionRunStep importRunStepInfo)
            {
                myRunSpace.Close();
                myPowerShell.Dispose();
                CloseImportConnectionResults CloseImportConnectionResults = new CloseImportConnectionResults();
                CloseImportConnectionResults.CustomData = "Import finished, closing powershell";
                return new CloseImportConnectionResults();
            }

            public int ImportMaxPageSize
            {
                get
                {
                    return m_importMaxPageSize;
                }
            }

            public int ImportDefaultPageSize
            {
                get
                {
                    return m_importDefaultPageSize;
                }
            }

            public void OpenExportConnection(KeyedCollection<string, ConfigParameter> configParameters,
                                Schema types,
                                OpenExportConnectionRunStep exportRunStep)
            {
                FIM.Extensions.Common.Library.PowershellUtils psUtils = new FIM.Extensions.Common.Library.PowershellUtils();

                //Get export type Full,Delta
                RunStepType = exportRunStep.ExportType;

                //Get export batch size
                m_exportMaxPageSize = exportRunStep.BatchSize;
                //log.Append("-OpenExportConnection:BatchSize = " + exportRunStep.BatchSize.ToString() + Environment.NewLine);

                //Get partition info
                RunStepPartition = exportRunStep.StepPartition.DN;

                if (configParameters.Contains(myParamStrings.runStepPowershellFileName) && !string.IsNullOrEmpty(configParameters[myParamStrings.runStepPowershellFileName].Value))
                { myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.runStepPowershellFileName].Value); }
                else if (configParameters.Contains(myParamStrings.exportPowershellFileName) && !string.IsNullOrEmpty(configParameters[myParamStrings.exportPowershellFileName].Value))
                { myPowershellFile = psUtils.resolvePSFileName(configParameters[myParamStrings.exportPowershellFileName].Value); }
                else { throw new FileNotFoundException("No export powershell script was specified"); }

                myPowerShell = PowerShell.Create();
                populateConnectionProperties(configParameters);
                myRunSpace = psUtils.OpenRunSpace(myRemoteServer, myUserName, myPassword);

            }

            public PutExportEntriesResults PutExportEntries(IList<CSEntryChange> csentries)
            {

                FIM.Extensions.Common.Library.exportEntries exportUtil = new FIM.Extensions.Common.Library.exportEntries();
                FIM.Extensions.Common.Library.PowershellUtils psUtils = new FIM.Extensions.Common.Library.PowershellUtils();

                //
                // The csentries parameter contains a collection of CSEntryChange
                // objects that need to be exported.  The number of CSEntryChange
                // objects is determined by the bacth size set on the Run Profile Step,
                // which contain be obtained from exportRunStep.BatchSize in OpenExportConnection().
                //

                foreach (CSEntryChange entryChange in csentries)
                {

                    string exportModificationType = null;                   //Contains the modification type to export {add,delete,replace,update}
                    string exportDN = null;                                 //Contains the DN of the object to export
                    Hashtable ChangedAttributes = new Hashtable();          //Contains all attributes that changes, [attributename], [changetype {add,delete,replace,update}]
                    Hashtable removedAttributeValues = new Hashtable();     //Contains all attribute values that should be removed from target, [attributename], [attributevalue]
                    Hashtable addedAttributeValues = new Hashtable();       //Contains all attribute values that should be addedto target, [attributename], [attributevalue]
                    Hashtable anchorAttributeValues = new Hashtable();      //Contains all anchor attributes for the object

                    //Update all HashTables from entryChange to send for powershell as input
                    exportUtil.UpdateExportHashTables(entryChange,
                        ref exportModificationType,
                        ref exportDN,
                        ref anchorAttributeValues,
                        ref ChangedAttributes,
                        ref addedAttributeValues,
                        ref removedAttributeValues);

                    ICollection<PSObject> psObjects = null;
                    string errorMsg = null;

                    myPowerShell.Commands.Clear();
                    psUtils.AddScriptFromFile(myPowershellFile, ref myPowerShell);
                    myPowerShell.AddParameter("ModificationType", exportModificationType);
                    myPowerShell.AddParameter("DN", exportDN);
                    myPowerShell.AddParameter("AnchorAttributes", anchorAttributeValues);
                    myPowerShell.AddParameter("ChangedAttributes", ChangedAttributes);
                    myPowerShell.AddParameter("AddValues", addedAttributeValues);
                    myPowerShell.AddParameter("RemoveValues", removedAttributeValues);
                    myPowerShell.AddParameter("RunStepType", RunStepType.ToString());
                    myPowerShell.AddParameter("RunStepPartition", RunStepPartition);

                    bool executedSuccessfully = psUtils.ExecutePowershell(myPowerShell, ref psObjects, ref errorMsg, false);

                    if (!executedSuccessfully) { throw new Exception(errorMsg); }

                }

                PutExportEntriesResults exportEntriesResults = new PutExportEntriesResults();
                return exportEntriesResults;
            }

            public void CloseExportConnection(CloseExportConnectionRunStep exportRunStep)
            {

                myRunSpace.Close();
                myPowerShell.Dispose();

            }

            public int ExportDefaultPageSize
            {
                get
                {
                    return m_exportDefaultPageSize;
                }
                set
                {
                    m_exportDefaultPageSize = value;
                }
            }

            public int ExportMaxPageSize
            {
                get
                {
                    return m_exportMaxPageSize;
                }
                set
                {
                    m_exportMaxPageSize = value;
                }
            }


        }
    }
}
